#include "Array_t.h"
#include "ArrayIterator_t.h"

const int INITIAL_CAPACITY = 4;

template<class T>
size_t Array_t<T>::expandVal = 4;

template<class T>
Array_t<T>::Array_t()
	: m_size(INITIAL_CAPACITY),
	  m_capacity(0),
	  m_impl(new T*[INITIAL_CAPACITY]()),
	  currCell(-1)
{
}

template<class T>
Array_t<T>::~Array_t()
{
	// Remove all elements
	removeAll();

	delete[] m_impl;
}

// COPY CTOR
template<class T>
Array_t<T>::Array_t(const Array_t& other)
{
	currCell = -1;
	m_size = other.m_size;
	m_capacity = 0;
	m_impl = new T*[m_size]();

	for(size_t i = 0; i < other.m_capacity; i++)
	{
		insert(other.m_impl[i]); 
	}

	if(m_capacity != other.m_capacity)
		throw "Array_t: copy ctor error";
}

template<class T>
Array_t<T>& Array_t<T>::operator=(const Array_t& other)
{
	if(this != &other)
	{
		m_size = other.m_size;
		m_capacity = 0;
		currCell = -1;
		delete[] m_impl;
		m_impl = new T*[m_size]();

		for(size_t i = 0; i < other.m_capacity; i++)
		{
			insert(other.m_impl[i]); 
		}

		if(m_capacity != other.m_capacity)
			throw "Array_t: operator= error";
	}

	return *this;
}

template<class T>
const T& Array_t<T>::operator[](int index) const
{
	if(index<0 || index >= (int)count())
		throw "Array_t: index out of bounds";

	return *(m_impl[index]);
}

template<class T>
T* Array_t<T>::next()
{
	if(currCell == m_capacity-1)
		return NULL;

	currCell++;
	return m_impl[currCell];
}

template<class T>
T* Array_t<T>::prev()
{
	if(currCell == -1)
		return NULL;

	if(currCell == 0)
	{
		currCell--;
		return NULL;
	}

	currCell--;

	return m_impl[currCell];
}

template<class T>
void Array_t<T>::reset()
{
	currCell = -1;
}

template<class T>
void Array_t<T>::doAppend(T* const elem, int index)
{
	if(isFull())
		if(!reallocArray())
			return;

	// shift element to the right, and make room for new element
	makeSpace(index);

	m_impl[index+1]=elem;
	m_capacity++;
	currCell = m_capacity-1;
}

template<class T>
void Array_t<T>::doRemove(const T& value)
{
	reduceSpace(currCell);

	m_capacity--;
	reset();
}

template<class T>
void Array_t<T>::doRemoveAll()
{
	for(size_t i = 0; i<m_capacity; i++)
		m_impl[i] = 0;

	reset();
	m_capacity = 0;
}

template<class T>
void Array_t<T>::doRemoveAndDeleteAll()
{
	for(size_t i = 0; i<m_capacity; i++)
	{
		delete m_impl[i];
		m_impl[i] = 0;
	}

	reset();
	m_capacity = 0;
}

template<class T>
Iterator_t<T>* Array_t<T>::iterator() const
{
	return new ArrayIterator_t<T>(*this);
}

template<class T>
bool Array_t<T>::isFull() const
{
	return m_capacity == m_size;
}

template<class T>
bool Array_t<T>::reallocArray()
{
	T** tmp = new T*[m_size + Array_t<T>::expandVal]();
	if(tmp == NULL)
		return false;

	for(size_t i=0; i<m_capacity; i++)
		tmp[i]=m_impl[i];

	m_size += Array_t<T>::expandVal;
	delete[] m_impl;
	m_impl = tmp;

	return true;
}

template<class T>
void Array_t<T>::makeSpace(int index)
{
	for(int i=m_capacity; i>index+1; i--)
	{
		m_impl[i]=m_impl[i-1];
	}
}

template<class T>
void Array_t<T>::reduceSpace(int index)
{
	size_t i=0;
	for(i=index; i<m_capacity-1; i++)
	{
		m_impl[i]=m_impl[i+1];
	}

	m_impl[i] = NULL;
}
